#include "vertexs.h"


#include <GL/glut.h>
#include <vector>
#include <cassert>

#include "coord.h"

/*
 * Representa un conjunto de Grupos de vertexs. 
 * Cada vertex esta compuesto por:
 *    2 coordenadas de textura
 *    3 coordenadas de normales
 *    3 coordenadas de posiciones
 *
 * Cada Grupo de vertexs agrupa a los vertexs que seran renderizados 
 * simultaneamente con un mismo modo.
 *
 * Ejemplo de uso, renderizado de un cuadrado y un triangulo:
 *
 *    //Primero definimos los grupos y sus vertexs.
 *    //Esto puede realizarse una sola vez.
 *    //O bien puede ser cambiado los parametros en tiempo de ejecucion
 *    Coord normal(0, 0, 1)
 *    Vertexs v;
 * 
 *    v.grupo(GL_QUADS);
 *    v.vertex(0, 0, normal, Coord(0, 0, 0));
 *    v.vertex(1, 0, normal, Coord(1, 0, 0));
 *    v.vertex(1, 1, normal, Coord(1, 1, 0));
 *    v.vertex(0, 1, normal, Coord(0, 1, 0));
 *
 *    v.grupo(GL_TRIANGLES);
 *    v.vertex(0, 0, normal, Coord(0, 0, 0));
 *    v.vertex(0, 0, normal, Coord(1, 0, 0));
 *    v.vertex(1, 1, normal, Coord(1, 1, 0));
 *
 *    v.cerrar()
 *
 *    //Renderizado
 *    v.enlazar()
 *    v.dibujar()
 *
 * */
Vertexs::Vertexs() : estaFinalizado(false) {}

/*
 * Agrega un nuevo grupo de vertexs asociados al modo de renderizado pasado
 * como parametro.
 *
 * Tipicamente el uso de este metodo equivale al glBegin de OpenGL.
 * */
void Vertexs::grupo(GLenum modo) {
   assert(not estaFinalizado);
   grupos.push_back(Grupo(modo, vertexArray.size() / 8));
}

/*
 * Vease la documentacion del constructor
 *
 * Equivale a glTexCoord, glNormal y glVertex
 * */
void Vertexs::vertex(float tex_s, float tex_t, const Coord &normal, const Coord &posicion) {
   assert(not estaFinalizado);
   vertexArray.push_back(tex_s);
   vertexArray.push_back(tex_t);

   vertexArray.push_back(normal.x());
   vertexArray.push_back(normal.y());
   vertexArray.push_back(normal.z());

   vertexArray.push_back(posicion.x());
   vertexArray.push_back(posicion.y());
   vertexArray.push_back(posicion.z());
}

/*
 * Una vez creado e inicializado la instancia Vertex, es necesario
 * cerrarla antes de dibujarla.
 * */
void Vertexs::cerrar() {
   assert(not estaFinalizado);
   grupo(GL_POINT); //Un grupo dummy para simplificar el algoritmo de dibujado

   estaFinalizado = true;
}

/*
 * Enlaza la instancia para su posterior renderizado.
 * La instancia debe estar cerrada (vease el metodo Vertexs::cerrar)
 *
 * A diferencia de otros casos, no existe un metodo "desenlazar()"
 * */
void Vertexs::enlazar() const {
   assert(estaFinalizado);
   glInterleavedArrays(GL_T2F_N3F_V3F, 0, &vertexArray[0]);
}

/*
 * Renderiza los Vertexs. La instancia debe estar cerrada y enlazada.
 * */
void Vertexs::dibujar() const {
   assert(estaFinalizado);

   std::vector<Grupo>::const_iterator it = grupos.begin();
   std::vector<Grupo>::const_iterator end = grupos.end();

   std::vector<Grupo>::const_iterator sig = grupos.begin();
   ++sig;

   for(; sig != end; ++it, ++sig) {
      glDrawArrays(it->first, it->second, (sig->second - it->second));
   }
}

const std::vector<float>& Vertexs::data(unsigned numGrupo, unsigned &pos, unsigned &cantCoords) const {
   assert(estaFinalizado and numGrupo < grupos.size() - 1);

   pos = grupos[numGrupo].second * 8;
   cantCoords = (grupos[numGrupo + 1].second - grupos[numGrupo].second) * 8;

   return vertexArray;
}

   size_t Vertexs::cantGrupos() const {
      if(estaFinalizado)
         return grupos.size() - 1;
      else
         return grupos.size();
   }

